using Engine;
using Engine.Graphics;
using Game;
using GameEntitySystem;
using TemplatesDatabase;

namespace SCIENEW {
    public class ComponentDriller : ComponentInventoryBase, IUpdateable, IDrawable {
        #region 接口实现
        public DrillerDevice.Mode Mode;
        public UpdateOrder UpdateOrder => UpdateOrder.Default;
        public int[] DrawOrders => [0];
        #endregion

        public ComponentBlockEntity m_componentBlockEntity;
        public SubsystemTerrain m_subsystemTerrain;
        public SubsystemEnginePower m_subsystemEnginePower;
        public SubsystemPickables m_subsystemPickables;
        public SubsystemProjectiles m_subsystemProjectiles;
        public SubsystemTime m_subsystemTime;
        public SubsystemAudio m_subsystemAudio;

        #region 钻杆渲染用
        float m_rotation;//钻头旋转角度
        int m_direction = 0;//机器的朝向
        BlockMesh[,] m_drillRodMeshesByFace = new BlockMesh[6,2];
        BlockMesh[] m_drillHeadMeshesByFace = new BlockMesh[6];
        PrimitivesRenderer3D m_primitivesRenderer = new();
        DrawBlockEnvironmentData m_drawBlockEnvironmentData = new();
        #endregion

        float m_depth = 0;

        Dictionary<int, float> m_rodsToSpeed;//钻杆与钻取速度的对应关系

        public int RodSlotIndex => 1;
        public int DrillerIndex => 0;
        public int RocksIndex => 2;

        public override int GetSlotCapacity(int slotIndex, int value) {
            if (slotIndex == RodSlotIndex) {
                if (m_rodsToSpeed.ContainsKey(value)) return base.GetSlotCapacity(slotIndex, value);//只允许钻杆放入
                return 0;
            }
            if (slotIndex == DrillerIndex) {
                if (Terrain.ExtractContents(value) == BaseDamageableItemBlock.Index && DamageableItemManager.GetItem(value) is DrillDItem) return base.GetSlotCapacity(slotIndex, value);//钻头
                return 0;
            }
            return base.GetSlotCapacity(slotIndex, value);
        }


        public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap) {
            base.Load(valuesDictionary, idToEntityMap);
            m_componentBlockEntity = Entity.FindComponent<ComponentBlockEntity>(true);
            m_subsystemTerrain = Project.FindSubsystem<SubsystemTerrain>(true);
            m_subsystemEnginePower = Project.FindSubsystem<SubsystemEnginePower>(true);
            m_subsystemPickables = Project.FindSubsystem<SubsystemPickables>(true);
            m_subsystemProjectiles = Project.FindSubsystem<SubsystemProjectiles>(true);
            m_subsystemTime = Project.FindSubsystem<SubsystemTime>(true);
            m_subsystemAudio = Project.FindSubsystem<SubsystemAudio>(true);
            Mode = valuesDictionary.GetValue("Mode", DrillerDevice.Mode.Drill);
            m_depth = valuesDictionary.GetValue("Depth", 0f);

            m_rodsToSpeed = new();
            m_rodsToSpeed.Add(ItemsBlockManager.GetItemIdByMaterial<RodItem>(Materials.Iron), 1f);
            m_rodsToSpeed.Add(ItemsBlockManager.GetItemIdByMaterial<RodItem>(Materials.Steel), 2f);

            Model model = ContentManager.Get<Model>("Models/DrillRod");
            Matrix boneAbsoluteTransform = BlockMesh.GetBoneAbsoluteTransform(model.FindMesh("DrillRod").ParentBone);
            Matrix boneAbsoluteTransform_Driller = BlockMesh.GetBoneAbsoluteTransform(model.FindMesh("Driller").ParentBone);
            for (int i = 0; i < 6; i++)
            {
                Matrix matrix = (i >= 4) ? ((i != 4) ? (Matrix.CreateRotationX((float)Math.PI) * Matrix.CreateTranslation(0.5f, 1f, 0.5f)) : Matrix.CreateTranslation(0.5f, 0f, 0.5f)) : (Matrix.CreateRotationX((float)Math.PI / 2f) * Matrix.CreateTranslation(0f, 0f, -0.5f) * Matrix.CreateRotationY((float)i * (float)Math.PI / 2f) * Matrix.CreateTranslation(0.5f, 0.5f, 0.5f));
                m_drillRodMeshesByFace[i,0] = new BlockMesh();
                m_drillRodMeshesByFace[i,0].AppendModelMeshPart(model.FindMesh("DrillRod").MeshParts[0], boneAbsoluteTransform * matrix, makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
                m_drillRodMeshesByFace[i,1] = new BlockMesh();
                m_drillRodMeshesByFace[i,1].AppendModelMeshPart(model.FindMesh("DrillRodShort").MeshParts[0], boneAbsoluteTransform * matrix, makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
                m_drillHeadMeshesByFace[i] = new BlockMesh();
                m_drillHeadMeshesByFace[i].AppendModelMeshPart(model.FindMesh("Driller").MeshParts[0], boneAbsoluteTransform_Driller * matrix, makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            }
        }

        public override void Save(ValuesDictionary valuesDictionary, EntityToIdMap entityToIdMap) {
            base.Save(valuesDictionary, entityToIdMap);
            valuesDictionary.SetValue("Mode", Mode);
            valuesDictionary.SetValue("Depth", m_depth);
        }

        public void Update(float dt) {
            //朝向
            int blockValue = m_subsystemTerrain.Terrain.GetCellValueFast(m_componentBlockEntity.Coordinates.X, m_componentBlockEntity.Coordinates.Y, m_componentBlockEntity.Coordinates.Z);
            m_direction = SixDirectionModelDevice.GetDirection_Static(blockValue);

            int rodCount = GetSlotCount(RodSlotIndex);
            float speed = GetSlotValue(RodSlotIndex) == 0 ? 0 : m_rodsToSpeed[GetSlotValue(RodSlotIndex)];
            bool isPowered = m_subsystemEnginePower.IsPowered(m_componentBlockEntity.Coordinates, out _);
            bool isEquippedDrill = GetSlotCount(DrillerIndex) > 0;
            //旋转
            if (isPowered) {
                m_rotation += dt * 2 * speed;
                m_rotation %= 2 * MathF.PI;
            }
            //探测基岩层
            int bedrockDepth = 0;
            while (bedrockDepth < 40) {
                Point3 p = m_componentBlockEntity.Coordinates - CellFace.FaceToPoint3(m_direction) * bedrockDepth;
                int testValue = m_subsystemTerrain.Terrain.GetCellValueFast(p.X, p.Y, p.Z);
                if (Terrain.ExtractContents(testValue) == BedrockBlock.Index) {
                    break;
                }
                bedrockDepth++;
            }
            //深度
            int requiredDepth = Math.Min(rodCount, bedrockDepth);
            if (isPowered && isEquippedDrill) {
                if (MathF.Abs(m_depth - requiredDepth) > 0.1f) {
                    m_depth = m_depth > requiredDepth ? m_depth - dt * speed : m_depth + dt * speed;
                    if (requiredDepth == 0) m_depth = 0;//没有钻杆，立马收回
                }
                else {
                    m_depth = requiredDepth;
                }
            }
            else {
                m_depth = 0;
            }

            //挖方块
            Vector3 vector = CellFace.FaceToVector3(m_direction);
            Point3 point = m_componentBlockEntity.Coordinates;
            int x = point.X;
            int y = point.Y;
            int z = point.Z;
            int[] array = [
                0,
                0,
                1,
                1,
                1,
                0,
                -1,
                -1,
                -1
            ], array2 = [
                0,
                -1,
                -1,
                0,
                1,
                1,
                1,
                0,
                -1
            ];
            Vector3 v = Vector3.Zero;
            if (isPowered && isEquippedDrill) {
                for (int l = 1; l <= m_depth + 1; l++) {
                    for (int m = 0; m < 9; m++) {
                        x = point.X - (int)vector.X * l;
                        y = point.Y - (int)vector.Y * l;
                        z = point.Z - (int)vector.Z * l;
                        if (vector.X != 0f) {
                            y = point.Y + array[m];
                            z = point.Z + array2[m];
                        }
                        if (vector.Y != 0f) {
                            x = point.X + array[m];
                            z = point.Z + array2[m];
                        }
                        if (vector.Z != 0f) {
                            x = point.X + array[m];
                            y = point.Y + array2[m];
                        }
                        DrillBlock(new Point3(x, y, z));
                    }
                }
            }

            //卸载
            if (m_subsystemTime.PeriodicGameTimeEvent(0.25f, 0.0) && Mode == DrillerDevice.Mode.Dispose) {
                int index = 0;
                foreach (var slot in m_slots) {
                    if (index++ <= RodSlotIndex) continue;
                    if (slot == null) continue;
                    if (slot.Count <= 0) continue;
                    if (m_subsystemProjectiles.FireProjectile(slot.Value,new Vector3(m_componentBlockEntity.Coordinates) + new Vector3(0.5f) + CellFace.FaceToVector3(m_direction) * 0.6f,CellFace.FaceToVector3(m_direction) * 40,Vector3.Zero,null) != null) {
                        m_subsystemAudio.PlaySound("Audio/DispenserShoot",1f,0f,new Vector3(m_componentBlockEntity.Coordinates),4f,true);
                    }
                    else {
                        m_subsystemPickables.AddPickable(slot.Value,1,new Vector3(m_componentBlockEntity.Coordinates) + new Vector3(0.5f) + CellFace.FaceToVector3(m_direction) * 0.5f,CellFace.FaceToVector3(m_direction) * 5,null);
                    }
                    slot.Count--;
                    break;
                }
            }
        }

        void DrillBlock(Point3 position) {
            int blockValue = m_subsystemTerrain.Terrain.GetCellValueFast(position.X, position.Y, position.Z);
            Block block = BlocksManager.Blocks[Terrain.ExtractContents(blockValue)];
            if (block is AirBlock or FluidBlock or BedrockBlock or FocalorsFluidBlock) return;

            m_subsystemTerrain.DestroyCell(
                0,
                position.X,
                position.Y,
                position.Z,
                0,
                true,
                false
            );
            int damage = DamageableItemManager.GetDamage(GetSlotValue(DrillerIndex));
            m_slots[DrillerIndex].Value = DamageableItemManager.SetDamage(GetSlotValue(DrillerIndex), damage + 1);
            if (BlocksManager.Blocks[BaseDamageableItemBlock.Index].GetBlockHealth(GetSlotValue(DrillerIndex)) < 0) m_slots[DrillerIndex].Count--;//损坏钻头

            blockValue = Terrain.ReplaceLight(blockValue, 0);
            int remain = AcquireItems(this, blockValue, 1);
            if (remain > 0)//接受不了，吐出物品
                m_subsystemPickables.AddPickable(
                    blockValue,
                    1,
                    new Vector3(m_componentBlockEntity.Coordinates) + new Vector3(0.5f) + CellFace.FaceToVector3(m_direction) * 0.5f,
                    CellFace.FaceToVector3(m_direction) * 5,
                    null
                );
        }

        public void Draw(Camera camera, int drawOrder) {
            //绘制最顶端的
            Point3 pos = m_componentBlockEntity.Coordinates;
            DrawingUtils.ModifyEnvironmentData(m_drawBlockEnvironmentData, m_subsystemTerrain, pos);

            Matrix rotationMatrix = Matrix.CreateTranslation(-DevicesUtils.NormalToVector3(m_direction) * 0.5f) * Matrix.CreateFromAxisAngle(CellFace.FaceToVector3(m_direction), m_rotation) * Matrix.CreateTranslation(DevicesUtils.NormalToVector3(m_direction) * 0.5f);
            Matrix matrix = rotationMatrix * Matrix.CreateTranslation(new Vector3(pos));

            if (GetSlotCount(RodSlotIndex) > 0 && DrawingUtils.IsVisible(m_subsystemTerrain, camera, new Vector3(pos))) BlocksManager.DrawMeshBlock(m_primitivesRenderer, m_drillRodMeshesByFace[m_direction,1], IndustrialModLoader.IETexture, Color.White, 1, ref matrix, m_drawBlockEnvironmentData);

            //绘制下端的
            for (int i = 0; i < (MathF.Ceiling(m_depth) == m_depth ? m_depth + 1 : MathF.Ceiling(m_depth)); i++) {
                if (i == 0) {
                    float firstLength = m_depth % 1;
                    Vector3 pos1 = m_direction is 0 or 1 or 4 ? new Vector3(pos) - CellFace.FaceToVector3(m_direction) * firstLength : new Vector3(pos) - CellFace.FaceToVector3(m_direction);
                    if (!DrawingUtils.IsVisible(m_subsystemTerrain, camera, pos1)) continue;
                    DrawingUtils.ModifyEnvironmentData(m_drawBlockEnvironmentData, m_subsystemTerrain, pos1);
                    Vector3 scale = Vector3.One - DevicesUtils.FaceToVector3Abs(m_direction) * (1 - firstLength);
                    Matrix matrix1 = Matrix.CreateScale(scale) * rotationMatrix * Matrix.CreateTranslation(pos1);
                    BlocksManager.DrawMeshBlock(m_primitivesRenderer, m_drillRodMeshesByFace[m_direction,0], IndustrialModLoader.IETexture, Color.White, 1, ref matrix1, m_drawBlockEnvironmentData);
                }
                else {
                    Vector3 posN = new Vector3(pos) - CellFace.FaceToVector3(m_direction) * (m_depth % 1 + i);
                    if (!DrawingUtils.IsVisible(m_subsystemTerrain, camera, posN)) continue;
                    Matrix matrixN = rotationMatrix * Matrix.CreateTranslation(posN);
                    DrawingUtils.ModifyEnvironmentData(m_drawBlockEnvironmentData, m_subsystemTerrain, posN);
                    BlocksManager.DrawMeshBlock(m_primitivesRenderer, m_drillRodMeshesByFace[m_direction,0], IndustrialModLoader.IETexture, Color.White, 1, ref matrixN, m_drawBlockEnvironmentData);
                }
            }

            //绘制钻头
            if (GetSlotCount(DrillerIndex) > 0) {
                Vector3 posH = new Vector3(pos) - CellFace.FaceToVector3(m_direction) * m_depth;
                Matrix headMatrix = rotationMatrix * Matrix.CreateTranslation(posH);
                DrawingUtils.ModifyEnvironmentData(m_drawBlockEnvironmentData, m_subsystemTerrain, posH);
                if (DrawingUtils.IsVisible(m_subsystemTerrain, camera, posH)) BlocksManager.DrawMeshBlock(m_primitivesRenderer, m_drillHeadMeshesByFace[m_direction], IndustrialModLoader.IETexture, Color.White, 1, ref headMatrix, m_drawBlockEnvironmentData);
            }


            m_primitivesRenderer.Flush(camera.ViewProjectionMatrix);
        }
    }
}